package cn.demoncat.util.pay.cmbc;

import cn.demoncat.util.exception.BizErrorRuntimeException;
import cn.demoncat.util.exception.RestRuntimeException;
import cn.demoncat.util.http.HttpUtil;
import cn.demoncat.util.http.entity.HttpRequest;
import cn.demoncat.util.lang.IDUtil;
import cn.demoncat.util.lang.LocalDateUtil;
import cn.demoncat.util.lang.MoneyUtil;
import cn.demoncat.util.lang.constant.StatusConstant;
import cn.demoncat.util.lang.entity.Pager;
import cn.demoncat.util.pay.cmbc.config.CmsbProperties;
import cn.demoncat.util.pay.cmbc.constant.CmsbApi;
import cn.demoncat.util.pay.cmbc.constant.CmsbLogs;
import cn.demoncat.util.pay.cmbc.entity.*;
import cn.demoncat.util.pay.constant.PayMsgs;
import cn.demoncat.util.xml.XmlUtil;
import cn.demoncat.util.xml.entity.XStreamClassAliasParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 民生银行银企直连
 *
 * @author 延晓磊
 * @since 2021年12月28日
 *
 * @see <a href="https://openbank.cmbc.com.cn/docs">民生银行文档中心</a>
 */
@Component
public class CmsbUtil {

    @Resource
    private CmsbProperties cmsbProperties;

    // 银行名称
    private static final String BANK_NAME = "民生银行";

    private static final Logger LOG = LoggerFactory.getLogger(CmsbUtil.class);


    /**
     * 查询余额
     *
     * 1、在15分钟内不能连续查询
     *
     * @return  余额
     *
     * @author 延晓磊
     * @since 2021年12月28日
     */
    public BigDecimal qryBal(){
        QryBalRequestBody param = new QryBalRequestBody();
        param.setTrnId(IDUtil.getTime());
        param.setAcntList(new QryBalRequestBody.Acnt());
        param.getAcntList().setAcntNo(cmsbProperties.getBankCard());
        return request(CmsbApi.qryBal, param, QryBalResponseParam.class).getXDataBody().getBalList().getBalInfo().getBalance();
    }

    /**
     * 查询明细
     *
     * @param start 开始条数，从1开始，包含
     * @param end   结束条数，最小为2，不包含，建议每次20条，即从21开始
     * @param date  查询日期：YYYY-MM-DD
     * @return  明细
     *
     * @author 延晓磊
     * @since 2021年12月28日
     */
    public Pager<QryDtlResponseParam.Body.DtlInfo> qryDtl(int start, int end, String date){
        // 请求
        QryDtlRequestBody param = new QryDtlRequestBody();
        param.setTrnId(IDUtil.getTime());
        param.setAcntNo(cmsbProperties.getBankCard());
        param.setStartNo(start);
        param.setEndNo(end);
        param.setDateFrom(date);
        param.setDateTo(date);
        QryDtlResponseParam result = request(CmsbApi.qryDtl, param, QryDtlResponseParam.class);
        // 转换
        Pager<QryDtlResponseParam.Body.DtlInfo> page = new Pager<>();
        page.setPageStart(start);
        page.setPageSize(end - start);
        page.setTotalCount(result.getXDataBody().getAllNum());
        page.setList(result.getXDataBody().getDtlList());
        return page;
    }

    /**
     * 查询明细
     *
     * @param start 开始条数，从1开始
     * @param date  查询日期：YYYY-MM-DD
     * @return  明细
     *
     * @author 延晓磊
     * @since 2021年12月28日
     */
    public List<QryDtlResponseParam.Body.DtlInfo> qryDtl(int start, String date){
        // 页长
        int size = 20;
        // 起始
        if (start < 1) {
            start = 1;
        }
        // 结束
        int end = start + size;
        // 查询
        int total = end;
        List<QryDtlResponseParam.Body.DtlInfo> list = new ArrayList<>();
        QryDtlResponseParam result;
        QryDtlRequestBody param = new QryDtlRequestBody();
        param.setAcntNo(cmsbProperties.getBankCard());
        param.setDateFrom(date);
        param.setDateTo(date);
        while (total >= start){
            // 查询
            param.setTrnId(IDUtil.getTime());
            param.setStartNo(start);
            param.setEndNo(end);
            result = request(CmsbApi.qryDtl, param, QryDtlResponseParam.class);
            list.addAll(result.getXDataBody().getDtlList());
            // 下一页
            total = result.getXDataBody().getAllNum();
            start = end;
            end += size;
        }
        return list;
    }

    /**
     * 查询明细数
     *
     * @param date  查询日期：YYYY-MM-DD
     * @return  明细数
     *
     * @author 延晓磊
     * @since 2021年12月28日
     */
    public int qryDtlCount(String date){
        QryDtlRequestBody param = new QryDtlRequestBody();
        param.setTrnId(IDUtil.getTime());
        param.setAcntNo(cmsbProperties.getBankCard());
        param.setStartNo(1);
        param.setEndNo(2);
        param.setDateFrom(date);
        param.setDateTo(date);
        return request(CmsbApi.qryDtl, param, QryDtlResponseParam.class).getXDataBody().getAllNum();
    }

    /**
     * 转账
     *
     * 注：交易后需使用转账查询，来查询此交易的最终状态
     *
     * @param insId         客户端唯一标志（转账ID）
     * @param acntToNo      收款账号
     * @param acntToName    收款人名称
     * @param bankCode      收款人开户行行号（总行/清算中心的联行号，同行非必填）
     * @param bankName      收款人开户行名称（支行全称）
     * @param localFlag     汇路：通常使用网银（5，实时、免费、限5万以下），对于不支持网银的小银行（网银转账失败）使用小额（2，两小时内，限5万）或大额（3，工作日半小时内，不限额）；同行忽略本参数，自动判断用 0本地 或 1异地
     * @param amount        转账金额 15,2
     * @param explain       摘要/用途 50
     *
     * @author 延晓磊
     * @since 2021年12月31日                     
     */
    public void xfer(String insId, String acntToNo, String acntToName, String bankCode, String bankName, String localFlag, BigDecimal amount, String explain){
        // 转账信息
        XferRequestBody param = new XferRequestBody();
        param.setTrnId(IDUtil.getTime());
        param.setInsId(insId);
        param.setAmount(MoneyUtil.toStr(amount));
        param.setExplain(explain);
        param.setLocalFlag(localFlag);
        // 付款人
        param.setAcntNo(cmsbProperties.getBankCard());
        // 收款人
        param.setAcntToNo(acntToNo);
        param.setAcntToName(acntToName);
        param.setBankCode(bankCode);
        param.setBankName(bankName);
        param.setExternBank(StatusConstant.Common.check(!bankName.contains(BANK_NAME)));
        if (StatusConstant.Common.isNo(param.getExternBank())) {
            // 同行：0本地、1异地
            param.setLocalFlag(StatusConstant.Common.check(!bankName.contains(cmsbProperties.getBankLocalFlag())));
        }
        // 发起请求
        request(CmsbApi.Xfer, param, ResponseParam.class);
    }

    /**
     * 转账查询
     *
     * @param insId         客户端唯一标志（转账ID）
     * @return null表示成功，其它 = 错误、处理消息
     *
     * @author 延晓磊
     * @since 2021年12月31日
     */
    public QryXferResponseParam.Body.StatusId qryXfer(String insId){
        QryXferRequestBody param = new QryXferRequestBody();
        param.setTrnId(IDUtil.getTime());
        param.setInsId(insId);
        QryXferResponseParam result;
        try {
            result = request(CmsbApi.qryXfer, param, QryXferResponseParam.class);
        }catch (Exception e){
            // 请求异常
            return QryXferResponseParam.Body.StatusId.error(e.getMessage());
        }
        if (result.getXDataBody().getStatusId().isSuccess()) {
            // 成功
            return null;
        }
        // 失败/处理中
        return result.getXDataBody().getStatusId();
    }



    /**
     * 发起请求
     *
     * @param api   接口
     * @param body  请求体
     * @param clazz 响应类型
     * @param <R>
     * @return
     *
     * @author 延晓磊
     * @since 2021年12月28日
     */
    private <R extends ResponseParam>R request(CmsbApi api, Object body, Class<R> clazz){
        // 请求参数
        RequestParam param = new RequestParam();
        param.setTrnCode(api.name());
        param.setXDataBody(body);
        param.setRequestHeader(new RequestParam.RequestHeader());
        param.getRequestHeader().setClientId(cmsbProperties.getClientId());
        param.getRequestHeader().setUserId(cmsbProperties.getUserId());
        param.getRequestHeader().setUserPswd(cmsbProperties.getUserPswd());
        param.getRequestHeader().setDtClient(LocalDateUtil.toStr());
        // 转换为XML
        String xml = XmlUtil.addDeclaration(XmlUtil.toXml(param, "CMBC"), "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
        // 请求银企直连代理
        LOG.info(CmsbLogs.REQUEST, api.getName(), api.name());
        LOG.debug(CmsbLogs.REQUEST_PARAM, api.getName(), xml);
        try {
            xml = HttpUtil.request(HttpRequest.post(cmsbProperties.getUrl()).xml(xml)).asString();
        }catch (Exception e){
            // 请求失败
            LOG.error(CmsbLogs.REQUEST_FAIL, api.getName(), api.name(), e.getMessage(), e);
            throw RestRuntimeException.apiError(PayMsgs.REQUEST_FAIL, e);
        }
        // 处理响应
        LOG.debug(CmsbLogs.REQUEST_RESULT, api.getName(), xml);
        R r = XmlUtil.toObj(xml, new XStreamClassAliasParam(clazz, "CMBC"));
        if (ResponseParam.ResponseHeader.Status.SUCCESS_CODE.equals(r.getResponseHeader().getStatus().getCode())) {
            // 成功
            r.setRequest(param);
            return r;
        }else if(ResponseParam.ResponseHeader.Status.UNKNOWN_CODE.equals(r.getResponseHeader().getStatus().getCode())){
            // 未知（转账）
            r.setRequest(param);
            return r;
        }else{
            // 失败
            LOG.warn(CmsbLogs.REQUEST_ERROR, api.getName(), api.name(), r.getResponseHeader().getStatus().getMessage());
            throw new BizErrorRuntimeException(PayMsgs.REQUEST_ERROR + r.getResponseHeader().getStatus().getMessage());
        }
    }

}
